//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.Collections;

/**************
 * This class needs to be re-evaluated
 * 12/5/2004
***********************/

namespace CommunityServer.Components
{
	/// <summary>
	/// Base class for creating Atom feeds
	/// </summary>
	public abstract class BaseAtomWriter : BaseSyndicationWriter
	{

		//Guid we append to the link. (ScottW - Move this to base)
		private string baseGuid = null;
		/// <summary>
		/// Constructs a writer for an RSS feed.
		/// </summary>
		/// <param name="posts">The entries to write as part of the feed.</param>
		public BaseAtomWriter(ArrayList posts, Section s, string baseUrl) : base(posts,s, baseUrl)
		{
			baseGuid = CSContext.Current.SiteSettings.PublicSiteKey.ToString();
		}

        protected string SiteGuid
        {
            get{return baseGuid;}
        }

		/// <summary>
		/// Output the feed base element.
		/// </summary>
		protected virtual void StartDocument()
		{
            // Write stylesheet for Atom feeds
            this.StyleSheet = Globals.GetSiteUrls().AtomStyleSheet();
            if (!Globals.IsNullorEmpty(this.StyleSheet))
            {
                this.WriteStyleSheet(this.StyleSheet);
            }

			this.WriteStartElement("feed");
			this.WriteAttributeString("xmlns", "http://www.w3.org/2005/Atom");
		}

		/// <summary>
		/// Default language posts are written in
		/// </summary>
		protected virtual string Language
		{
			get { return "en-US";}
		}

		/// <summary>
		/// When was this feed last updated
		/// </summary>
		protected abstract DateTime Modified {get;}

		/// <summary>
		/// Link to the home page which corresponds to this Feed
		/// </summary>
		protected abstract string HomeLink {get;}

		/// <summary>
		/// Output the Atom namespaces used in the feed.
		/// </summary>
		protected virtual void SetNamespaces()
		{
			//this.WriteAttributeString("xmlns", "http://purl.org/atom/ns#");
			this.WriteAttributeString("xml:lang", Language);
		}

		/// <summary>
		/// Writes the channel information usualling using BuildChannel
		/// </summary>
		protected abstract void WriteChannel();

		/// <summary>
		/// Called by the base class to create the feed once the 
		/// metadata has been retrieved
		/// </summary>
		protected override void Build()
		{

			StartDocument();
			SetNamespaces();
			WriteChannel();
			foreach(Post post in this.posts)
			{
				StartEntry();
				WriteEntry(post);
				EndEntry();
			}
			EndDocument();
		}

        protected abstract string FeedId
        {
            get;
        }

        protected abstract string SelfUrl
        {
             get;
        }

        

		/// <summary>
		/// Actually build the header contents of the feed.
		/// </summary>
		/// <param name="title">The title element of the feed.</param>
		/// <param name="link">The link element of the feed.</param>
		/// <param name="description">The description element of the feed.</param>
		protected void BuildChannel(string title, string link, string description)
		{
            this.WriteStartElement("title");
                this.WriteAttributeString("type","html");
                this.WriteString(title);
            this.WriteEndElement();

            this.WriteStartElement("subtitle");
            this.WriteAttributeString("type","html");
            this.WriteString(description);
            this.WriteEndElement();

            this.WriteElementString("id",SelfUrl);

			this.WriteStartElement("link");
			this.WriteAttributeString("rel", "alternate");
			this.WriteAttributeString("type", "text/html");
			this.WriteAttributeString("href", link);
			this.WriteEndElement();

            this.WriteStartElement("link");
            this.WriteAttributeString("rel", "self");
            this.WriteAttributeString("type", "application/atom+xml");
            this.WriteAttributeString("href", SelfUrl);
            this.WriteEndElement();

            //Default implementation is empty since we support multiple authors by default
            FeedAuthor();		
			
			this.WriteStartElement("generator");
			this.WriteAttributeString("uri", "http://communityserver.org");
			this.WriteAttributeString("version", SiteStatistics.CommunityServerVersion.ToString());
			this.WriteString("Community Server");
			this.WriteEndElement();

			this.WriteElementString("updated", W3UTCZ(Modified));
		}

        protected virtual void FeedAuthor()
        {
            
        }

		/// <summary>
		/// starts a new post
		/// </summary>
		protected virtual void StartEntry()
		{
			this.WriteStartElement("entry");
		}

		/// <summary>
		/// Gets the link for an individual post. This will different by application
		/// </summary>
		/// <param name="p"></param>
		/// <returns></returns>
		protected abstract string BuildLink(Post p);


		/// <summary>
		/// Different application may be able to support the modified and issued elements
		/// </summary>
		/// <param name="p"></param>
		protected virtual void DateElements(Post p)
		{
			this.WriteElementString("published", W3UTCZ(p.PostDate.ToUniversalTime()));
			this.WriteElementString("updated", W3UTCZ(p.PostDate.ToUniversalTime()));
		}

		/// <summary>
		/// Different applications may want to filter/update/modify the data syndicated
		/// </summary>
		/// <param name="p"></param>
		/// <returns></returns>
		protected virtual string PostBody(Post p)
		{
			return p.RenderedBody(PostTarget.Syndication);
		}

		/// <summary>
		/// Enables new elements to be created
		/// </summary>
		/// <param name="p"></param>
		protected virtual void ExtendendElements(Post p)
		{}

		/// <summary>
		/// Writes a single post out to the feed
		/// </summary>
		/// <param name="p"></param>
		protected virtual void WriteEntry(Post p)
		{

			// Subject is already HtmlEncoded in DB, so write raw
            this.WriteElementString("title", Globals.HtmlDecode(p.Subject));
			
			this.WriteStartElement("link");
			this.WriteAttributeString("rel", "alternate");
			this.WriteAttributeString("type", "text/html");
			this.WriteAttributeString("href", BuildLink(p));
			this.WriteEndElement();

            if(p.HasAttachment && p.Attachment != null)
            {
                
                this.WriteStartElement("link");
                this.WriteAttributeString("rel", "enclosure");
                this.WriteAttributeString("type", p.Attachment.ContentType);
                this.WriteAttributeString("length",p.Attachment.Length.ToString());
                this.WriteAttributeString("href", EnclosureUrl(p.Attachment));
                this.WriteEndElement();
            }

			this.WriteElementString("id",BuildLink(p));

			DateElements(p);

			this.WriteStartElement("content");
			this.WriteAttributeString("type","html");
			this.WriteString(PostBody(p));
			this.WriteEndElement();

		    EntryAuthor(p);

		    ExtendendElements(p);

		}

	    protected virtual void EntryAuthor(Post p)
	    {
	        this.WriteStartElement("author");
	        this.WriteElementString("name",p.Username);
	        this.WriteElementString("uri",this.BaseUrl + Globals.GetSiteUrls().UserProfile(p.Username));
	        this.WriteEndElement();
	    }

	    /// <summary>
		/// Output the end tag for the entry.
		/// </summary>
		protected void EndEntry()
		{
			this.WriteEndElement();
		}

		/// <summary>
		/// Output the end tag for the feed.
		/// </summary>
		protected void EndDocument()
		{
			this.WriteEndElement();
		}


		#region TimeHelpers

		/// <summary>
		/// Format a timezone for the feed.
		/// </summary>
		/// <param name="tz">The timezone.</param>
		/// <returns></returns>
		private static string TimeZone(double tz)
		{
			if (tz < 0)
			{
				return tz.ToString("00") + ":00";
			}
			else
			{
				return "+" + tz.ToString("00") + ":00";
			}

		}

		/// <summary>
		/// Format a date & timezone pair.
		/// </summary>
		/// <param name="dt">The date/time.</param>
		/// <param name="tz">The timezone.</param>
		/// <returns></returns>
		private static string W3UTC(DateTime dt, string tz)
		{
			return dt.ToString("yyyy-MM-ddTHH:mm:ss") + tz;
		}

		/// <summary>
		/// Format a date.
		/// </summary>
		/// <param name="dt">The date/time.</param>
		/// <returns></returns>
		private static string W3UTCZ(DateTime dt)
		{
			return dt.ToString("yyyy-MM-ddTHH:mm:ssZ");
		}
		#endregion
	}
}
